package com.jeasonzhao.commons.utils;

import com.jeasonzhao.commons.basic.IntegerPair;

public class DataTypes extends IntegerPair
{
    private static final long serialVersionUID = 1L;
    public static final DataTypes STRING = new DataTypes(10,String.class,"String","string,char,varchar,str,varchar2");
    public static final DataTypes BOOL = new DataTypes(20,Boolean.class,"Boolean","boolean,bool");
    public static final DataTypes DOUBLE = new DataTypes(30,Double.class,"Float","double,float,decimal,numeric");
    public static final DataTypes INT = new DataTypes(40,Integer.class,"Integer","int,integer,number,num,long");
    public static final DataTypes DATE = new DataTypes(50,java.util.Date.class,"DateTime","date,time,datetime,timestamp");
    public static final DataTypes OBJECT = new DataTypes(60,Object.class,"Object",null);
    public static final DataTypes ARRAY = new DataTypes(70,java.util.ArrayList.class,"Object Array","array,list,arraylist,collection,collections,vector,set");
    public static final DataTypes VOID = new DataTypes(10240,java.lang.Void.class,"Void","null");
//    public static final DataTypes HASH = new DataTypes(80,java.util.HashMap.class,"Hash??");
    private Class<?> m_classInitType = null;
    private Class<?> m_classComponentType = null;

    public int hashCode()
    {
        return super.hashCode();
    }

    private DataTypes(DataTypes t)
    {
        super(t);
        if(null != t)
        {
            this.m_classComponentType = t.m_classComponentType;
            this.m_classInitType = t.m_classInitType;
        }
    }

    private DataTypes(int nId,Class<?> cls,String strName,String strAliasNames)
    {
        super(nId,strName,strAliasNames);
        m_classInitType = null == cls ? Object.class : cls;
    }

    public static DataTypes createArrayType(Class<?> cls)
    {
        return DataTypes.from(java.lang.reflect.Array.newInstance(cls,0).getClass());
    }

    public DataTypes setComponentType(Class<?> cls)
    {
        m_classComponentType = cls;
        return this;
    }

    public DataTypes setInitType(Class<?> cls)
    {
        m_classInitType = cls;
        return this;
    }

    public Class<?> getComponentType()
    {
        return m_classComponentType == null ? Object.class : m_classComponentType;
    }

    public Class<?> getInitType()
    {
        return m_classInitType;
    }

    public static DataTypes from(String strName)
    {
        DataTypes dt = (DataTypes) findConstant(DataTypes.class,strName,null);
        if(null == dt)
        {
            dt = DataTypes.OBJECT;
        }
        return dt;
    }

    public static DataTypes from(Class<?> clsInit)
    {
        if(null == clsInit)
        {
            return null;
        }
        else if(clsInit.isArray()
                || java.util.Collection.class.isAssignableFrom(clsInit))
        {
            return(new DataTypes(DataTypes.ARRAY))
                .setInitType(clsInit)
                .setComponentType(clsInit.getComponentType() == null ? Object.class : clsInit.getComponentType());
        }
        else
        {
            clsInit = box(clsInit);
            Class<?> cls = unbox(clsInit);
            if(String.class.isAssignableFrom(cls)
               || java.lang.CharSequence.class.isAssignableFrom(cls))
            {
                return new DataTypes(STRING).setInitType(clsInit);
            }
            else if(Boolean.class.isAssignableFrom(cls))
            {
                return new DataTypes(BOOL).setInitType(clsInit);
            }
            else if(Integer.class.isAssignableFrom(cls))
            {
                return(new DataTypes(INT)).setInitType(clsInit);
            }
            else if(Double.class.isAssignableFrom(cls)
                    || Number.class.isAssignableFrom(cls)
                )
            {
                return(new DataTypes(DOUBLE)).setInitType(clsInit);
            }
            else if(java.util.Date.class.isAssignableFrom(cls))
            {
                return new DataTypes(DATE).setInitType(clsInit);
            }
            else if(cls == Void.TYPE || Void.class.isAssignableFrom(cls))
            {
                return new DataTypes(VOID).setInitType(clsInit);
            }
            else
            {
                return new DataTypes(OBJECT).setInitType(clsInit);
            }
        }
    }

    private static final Class<?> unbox(Class<?> type)
    {
        if(null == type)
        {
            return type;
        }
        type = box(type);
        if(Boolean.class.isAssignableFrom(type))
        {
            return Boolean.class;
        }
        else if(Byte.class.isAssignableFrom(type)
                || Short.class.isAssignableFrom(type)
                || Integer.class.isAssignableFrom(type)
                || Long.class.isAssignableFrom(type)
                || Character.class.isAssignableFrom(type)
            )
        {
            return Integer.class;
        }
        else if(Float.class.isAssignableFrom(type)
                || Double.class.isAssignableFrom(type)
            )
        {
            return Double.class;
        }
        else
        {
            return type;
        }
    }

    private static final Class<?> box(Class<?> type)
    {
        if(null == type || type.isPrimitive() == false)
        {
            return type;
        }
        if(type == Boolean.TYPE)
        {
            return Boolean.class;
        }
        else if(type == Byte.TYPE)
        {
            return Byte.class;
        }
        else if(type == Short.TYPE)
        {
            return Short.class;
        }
        else if(type == Integer.TYPE)
        {
            return Integer.class;
        }
        else if(type == Long.TYPE)
        {
            return Long.class;
        }
        else if(type == Float.TYPE)
        {
            return Float.class;
        }
        else if(type == Double.TYPE)
        {
            return Double.class;
        }
        else if(type == Character.TYPE)
        {
            return Character.class;
        }
        else
        {
            return type;
        }
    }

    public String toString()
    {
        if(this.isArray())
        {
            return "{" + getConstantFieldName() + "(" + this.m_classComponentType + "):" + this.getId() + "=" + this.getName() + "}";
        }
        else
        {
            return super.toString();
        }
    }

    public boolean isPrimitive()
    {
        return this.equals(STRING)
            || this.equals(BOOL)
            || this.equals(DOUBLE)
            || this.equals(INT)
            || this.equals(DATE)
            ;
    }

    public boolean isNumeric()
    {
        return this.equals(DOUBLE) || this.equals(INT);
    }

    public boolean isString()
    {
        return this.equals(STRING);
    }

    public boolean isInteger()
    {
        return this.equals(INT);
    }

    public boolean isDouble()
    {
        return this.equals(DOUBLE);
    }

    public boolean isDate()
    {
        return this.equals(DATE);
    }
    public boolean isBoolean()
    {
        return this.equals(BOOL);
    }
    public boolean isArray()
    {
        return this.equals(ARRAY);
    }

    public Object castValue(Object objV)
    {
        return Algorithms.castValue(this,objV);
    }

    public static boolean isArray(Class<?> cls)
    {
        return from(cls).isArray();
    }

    public static boolean isString(Class<?> cls)
    {
        return from(cls).equals(STRING);
    }

    public static boolean isNumeric(Class<?> cls)
    {
        if(null == cls)
        {
            return false;
        }
        DataTypes dt = DataTypes.from(cls);
        return dt.getId() == INT.getId() || dt.getId() == DOUBLE.getId();
    }

    public static boolean isPrimitive(Class<?> cls)
    {
        return DataTypes.from(cls).isPrimitive();
    }
    public static boolean isBoolean(Class<?> cls)
        {
            return DataTypes.from(cls).isBoolean();
    }
    public static boolean isDate(Class<?> cls)
    {
        return DataTypes.from(cls).isDate();
    }
}
